MV3: Failed to send SharedArrayBuffer to Offscreen Document

584 views
Skip to first unread message

Svyatoslav Alekseev

unread,
Mar 19, 2025, 10:41:15 AM3/19/25
to Chromium Extensions
In Chrome Extensions v3, the object pointing to SAB comes empty or does not come at all (invisible error). In MV2, everything worked with specified CORS between the background page and workers. Is there a working way to share raw binary data buffer between service_worker and chrome.offscreen?
---------------------
manifest.json:
----
{"name":"SharedArrayBuffer",
"short_name":"SAB",
"version": "3.0.0.0",
"manifest_version":3,
"cross_origin_embedder_policy":{"value": "require-corp"},
"cross_origin_opener_policy":{"value": "same-origin"},
"minimum_chrome_version":"120.0",
"background": {"service_worker": "sw.js"},
"permissions":["offscreen"],
"host_permissions":["<all_urls>"]
}
------------------
sw.js
----
chrome.offscreen.createDocument({url:chrome.runtime.getURL('offscreen.html'),reasons: ['WORKERS'],justification:'wtf',});
const BT=new BroadcastChannel('BT');BT.onmessage=e=>{console.log(e.data)}
chrome.runtime.onMessage.addListener((m)=>{console.log(m)});
async function MC(){
const mc=new MessageChannel();doc=(await clients.matchAll({includeUncontrolled:true})).find(c=>c.url.endsWith('/offscreen.html'));
doc.postMessage('ping',[mc.port2]);
mc.port1.onmessage=e=>{console.log(e.data)}
}
setTimeout(MC,99);

--------------
offscreen.html
---
<!DOCTYPE HTML>
<html><head><meta charset=utf-8><title></title><script src=offscreen.js></script></head></html>
------------------
offscreen.js
---
console.log(crossOriginIsolated);
const SAB=new SharedArrayBuffer(12),U32A=new Uint32Array(SAB),BT=new BroadcastChannel('BT');
BT.postMessage('TEST0');BT.postMessage(SAB);
chrome.runtime.sendMessage('TEST1');chrome.runtime.sendMessage(SAB);
navigator.serviceWorker.onmessage=e=>{
e.ports[0].postMessage('TEST2');e.ports[0].postMessage(SAB);
}

woxxom

unread,
Mar 19, 2025, 8:53:08 PM3/19/25
to Chromium Extensions, Svyatoslav Alekseev
99ms may be insufficient, instead you should "await" on createDocument, see also a complete example for Blob in https://stackoverflow.com/a/77427098

Svyatoslav Alekseev

unread,
Mar 22, 2025, 6:32:40 AM3/22/25
to Chromium Extensions, woxxom
I added the line to my example
const blob = new Blob(['foo']);e.ports[0].postMessage(blob); as linked in your example.
Passing Blob works, SharedArrayBuffer does not. Apparently it is impossible to pass SAB between backgound and offscreen. But after many years of such answers, no one has yet received an answer with a working example or from the development team. I just need to know, if it is impossible, then I will accept the fact that I will have to rework the MV2 extension for MV3 for a few more months, it is a trifle compared to the 10 months I have already spent. Is it possible to tag or invite the programmer responsible for this functionality to the conversation?

четверг, 20 марта 2025 г. в 03:53:08 UTC+3, woxxom:

Svyatoslav Alekseev

unread,
Mar 22, 2025, 8:24:46 AM3/22/25
to Chromium Extensions, woxxom
A reward of $100 USA (most likely in crypto) to the first (before the end of the month) to write a working example of an extension with SAB transfer for general use between servise_worker and offscreen document. Should not violate Chrome Web Store rules and not sandbox, tab parsing API should work.
Publish the code here, to receive the reward write to svyatal...@gmail.com

суббота, 22 марта 2025 г. в 13:32:40 UTC+3, Svyatoslav Alekseev:

Svyatoslav Alekseev

unread,
Mar 22, 2025, 8:27:33 AM3/22/25
to Chromium Extensions, Svyatoslav Alekseev, woxxom
svyataleseev88 @ gmail.com

суббота, 22 марта 2025 г. в 15:24:46 UTC+3, Svyatoslav Alekseev:

Mythical 5th

unread,
Mar 22, 2025, 11:30:43 AM3/22/25
to Chromium Extensions, Svyatoslav Alekseev, woxxom
The docs state that sendMessage() lets you send a one-time JSON-serializable message. It can't even send a true Date object, so it's not a surprise to see it not send your SAB.

Since you'd not included runtime.connect() in your code example I tried that and got the same result. However, it does send the Uint32Array (serialized), and is designed to be used as "a reusable long-lived message passing channel," so it might work for you and be the least bad option.



offscreen:
const SAB = new SharedArrayBuffer(12),
  U32A = new Uint32Array(SAB);

console.log("sab", SAB, "u32a", U32A);

chrome.runtime.onConnect.addListener(port =>
{
  port.postMessage({a: SAB, b: new Date(), c: U32A});
});


sw:
chrome.offscreen.createDocument({
  url: chrome.runtime.getURL('offscreen.html'),
  reasons: ['WORKERS'],
  justification: 'wtf'
}, () =>
{
  const messagePort = chrome.runtime.connect();
  messagePort.onMessage.addListener(msg =>
  {
    console.log("msg", msg)
  });
});

al

unread,
Mar 22, 2025, 12:14:20 PM3/22/25
to Chromium Extensions, Mythical 5th, Svyatoslav Alekseev, woxxom

In addition to what Mythical 5th said about serialization, if I were you, I would assume it's not supported. 
Looking at the short doc, the bottom section seems to indicate that crossOriginIsolation isn't fully implemented in Service Workers. 
The bug tracker it links is labelled as fixed, but it looks like some commenters don't believe it is. There are also some emails of the devs that worked on that issue (do what you will with that info, I wouldn't myself).

Regardless, it doesn't seem like an Extension service worker can be crossOrigin isolated, given, even with the manifest keys present, it still returns false when checking. 
Meaning, even if your Offscreen doc is isolated (which, with the keys present, it is), so long as the SW isn't, you can't share the SAB, using something like postMessage.

Since an MV2 Background Page is just that, a page, rather than an SW, it works fine. 

That's what I got from gleaming MDN and Chrome dev docs anyway. As I said up top, I'd just assume it's not supported, at least unless the DevRel folk say otherwise.

Svyatoslav Alekseev

unread,
Mar 22, 2025, 12:48:57 PM3/22/25
to Chromium Extensions, Mythical 5th, Svyatoslav Alekseev, woxxom
Unfortunately, SAB is required to pause the Worker (opened via offscreen) that is busy with continuous calculations, because service_worker and offscreen-worker are working on stages of one task and the costs (timer pause) for listening and sending messages will take 30% of the speed. Continuous calculations cannot even be paused without SAB. Only SharredArrayBuffer can be used as a simple if() (with a value from another thread) for continuous calculations. I just can't believe that MV3 developers forgot about 8 years of parallelism, but I can already guess, because offscreen works on the same core with the background script. At the same time, SAB works in offscreen and its Worker, but I have a huge array in memory stored in service_worker that is needed for the tabs API to work and again a stalemate (like in chess). $100 is still waiting for its hero.
суббота, 22 марта 2025 г. в 18:30:43 UTC+3, Mythical 5th:

Svyatoslav Alekseev

unread,
Mar 22, 2025, 12:55:35 PM3/22/25
to Chromium Extensions, al, Mythical 5th, Svyatoslav Alekseev, woxxom
You have confirmed my fears even more. The MV3 developers have abandoned 8 years of concurrency gains. However, I have noticed that SAB works in offscreen and the Workers it spawned. I would have liked to have known this 10 months ago when I started migrating to MV3. Now I have a huge amount of work to redo. $100 is still waiting for its hero, but after your reply I am almost certain there is no solution.
суббота, 22 марта 2025 г. в 19:14:20 UTC+3, al:

woxxom

unread,
Mar 22, 2025, 6:50:44 PM3/22/25
to Chromium Extensions, Svyatoslav Alekseev, al, Mythical 5th, woxxom
> sendMessage() lets you send a one-time JSON-serializable message. It can't even send a true Date object, so it's not a surprise to see it not send your SAB.

This has nothing to do with the problem, because the author uses Web platform messaging, which supports SAB.
The problem is either a bug in Chrome or an intended requirement to enable COOP/COEP, which can be done in manifest.json if I remember correctly, try googling.

Mythical 5th

unread,
Mar 23, 2025, 8:19:06 AM3/23/25
to Chromium Extensions, woxxom, Svyatoslav Alekseev, al, Mythical 5th
He tried sendMessage
chrome.runtime.sendMessage('TEST1');chrome.runtime.sendMessage(SAB);

Message has been deleted

Svyatoslav Alekseev

unread,
Mar 27, 2025, 11:02:11 AM3/27/25
to Chromium Extensions
I created a regression report:
https://issues.chromium.org/issues/406662639
воскресенье, 23 марта 2025 г. в 15:19:06 UTC+3, Mythical 5th:

woxxom

unread,
Mar 27, 2025, 10:57:00 PM3/27/25
to Chromium Extensions, Svyatoslav Alekseev
Looks like it was closed by accident by some random chromium developer. If it's not re-opened in a couple of days, consider making a new report and attach  your extension in a zip file, not as text.

al

unread,
Jan 2, 2026, 6:45:21 AMJan 2
to Chromium Extensions
Hoping to seek advice from Google folk - any work or plans to do so, happening in this area?
Similar to the author, I'd like to use SABs and messaging between my SW and whatever other context that's appropriate to facilitate the transfer of large swaths of binary data. 

For instance, transferring a tabCapture (ordinary weight @ ~700KB) takes ~250ms. Those numbers will vary of course, but I'd naively think that postMessage + SAB would be a lot quicker.
Even with unpacking the dataURL, etc, etc.

So specifically, enabling postMessage transfer to/from the SW.
And (my additional q) if it'll be transferrable to/from an isolated content script environment.

Oliver Dunk

unread,
Jan 2, 2026, 6:54:27 AMJan 2
to al, Chromium Extensions
I'm not sure why this was closed - I've moved it to an extensions component and re-opened it.

Given the limited activity on this one I'm not sure it is something we will prioritize soon. That said, we are interested in improving extension messaging in general, for example supporting structured clone and eventually maybe even transferables (though how to do this is less clear) in the extension messaging APIs. That may be another solution to your use case. I don't have a timeline for that, but with the other changes we have been focused on about to ship it may be something we can start to make early related changes for.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/f5d7d8c1-161b-49db-820a-e9a59450b37en%40chromium.org.

al

unread,
Jan 2, 2026, 7:03:30 AMJan 2
to Chromium Extensions, Oliver Dunk, Chromium Extensions, al
Thanks Oliver. 

Definitely looking forward to those changes, and I saw the structure clone discussion which is certainly promising as well.

Thanks for the update. 

woxxom

unread,
Jan 2, 2026, 9:40:11 AMJan 2
to Chromium Extensions, al, Oliver Dunk, Chromium Extensions
Until SAB is fixed you can already use the web platform messaging via navigator.serviceWorker and Blob which being a handle is sent instantly. Accessing the actual data would take time of course but it'll still be like 10x faster than the current JSON-based extension messaging anyway.

al

unread,
Jan 2, 2026, 7:39:04 PMJan 2
to Chromium Extensions, woxxom, al, Chromium Extensions
Do you mind elaborating a bit?
I can see transferrables would work with that approach, but can't seem to find mentions of Blob.

I think I've gotten ahead of myself anyway, perhaps transferrables is all I need.

To share it from a content script context to the service worker, my only guess would be to create an iframe,
then call serviceworker.register(..) from there. Is there a simpler approach?

woxxom

unread,
Jan 3, 2026, 1:12:03 AMJan 3
to Chromium Extensions, al, woxxom, Chromium Extensions
>  transferrables 

This is only when you actually transferring the data via the second parameter of web platform messaging. When you don't, anything cloneable can be sent including Blob and File which are just handles to the actual data.

>  from a content script context to the service worker

My comment above about navigator.serviceWorker applies to extension's own pages/frames, popup, offscreen document. For a content script, which runs in the web page process, you'll need a web_accessible_resources iframe which being a extension process context can use navigator.serviceWorker to relay the MessagePort through which your content script could talk directly to the service worker, no need to register anything additionally. Here's one example out of many: https://stackoverflow.com/questions/68687624/communication-between-content-script-and-web-accessible-resources-iframe/68689866#68689866:~:text=Web%20messaging

al

unread,
Jan 4, 2026, 6:54:56 PMJan 4
to Chromium Extensions, woxxom, al, Chromium Extensions
Ah I see, interesting. I obviously have a lot to research in that space, that's good to know.
I think I might still transfer just to save on memory.

Yeah sorry, I should have lead into that part. I thought I'd have to re/register it as when I was briefly testing with an iframe and the serviceworker.controller was undefined at the time.
Thanks for the link, looks like it does exactly what I'm after.

Thanks Woxxom.

al

unread,
Jan 17, 2026, 10:04:52 PM (5 days ago) Jan 17
to Chromium Extensions, woxxom, Chromium Extensions
@woxxom

Are you aware of any restrictions/limitations posed by incognito contexts?
It seems like within an iframe in an incognito page, serviceWorker.ready never resolves.

At the moment, I can't find any reason why it may be.

woxxom

unread,
Jan 18, 2026, 2:20:27 AM (5 days ago) Jan 18
to Chromium Extensions, al, woxxom, Chromium Extensions

al

unread,
Jan 18, 2026, 5:31:23 AM (5 days ago) Jan 18
to woxxom, Chromium Extensions
Good call, using split mode causes it to resolve.
I wondered if it had something to do with incognito creating a separate context.

I don't use split currently and I'm not sure I'm comfortable doing so, given concurrency/race issues..

> Because incognito tabs cannot use this shared process, an extension using the "spanning" incognito mode will not be able to load pages from its extension package into the main frame of an incognito tab. 
I have to be misinterpretting this, sa loading an iframe with an extension document works, it's just accessing the SeviceWorker registration that fails. 

I've since got a work-around using sendMessage, but that sucks given transfer latency.

Thanks Woxxom. It may be worth updating you SO answer regarding incognito, etc. 

woxxom

unread,
Jan 18, 2026, 7:14:44 AM (5 days ago) Jan 18
to Chromium Extensions, al, Chromium Extensions, woxxom
Ok, but it sounds like a bug, because this is an iframe, not "the main frame of an incognito tab".

woxxom

unread,
Jan 18, 2026, 7:17:29 AM (5 days ago) Jan 18
to Chromium Extensions, woxxom, al, Chromium Extensions
OTOH probably not as the underlying reason is the same - an incognito container cannot embed any non-incognito processes anywhere.

al

unread,
Jan 18, 2026, 9:08:35 AM (5 days ago) Jan 18
to woxxom, Chromium Extensions
That's too bad. Makes sense though, I guess.
Fortunately I can work around it with a penalty, but the next person may not.

I feel like postMessage in incognito for the price of running two service workers (?) isn't worth it.

Regardless, given most of the time my extension is in non-incognito, your linked solution has worked a treat (perf-wise).
1ms transfer of 200kb is nothing to sneeze at. 

I think my only other gripe is having to add the message listener on wake. 
Though it looks like you can still dynamically add it.. I think I'll stick to that for now.
Reply all
Reply to author
Forward
0 new messages