@@ -15,46 +15,119 @@ export const SHARED_WORKER_WINDOW_PATH = '/oro/shared-worker/index.html'
1515
1616export const channel = new BroadcastChannel ( 'oro.runtime.sharedWorker' )
1717export const workers = new Map ( )
18+ const workerReferences = new Map ( )
1819
1920channel . addEventListener ( 'message' , ( event ) => {
20- if ( event . data ?. error ?. id ) {
21- const ref = workers . get ( event . data . error . id )
22- if ( ref ) {
23- const worker = ref . deref ( )
24- if ( ! worker ) {
21+ if ( event . data ?. error ?. id !== null && event . data ?. error ?. id !== undefined ) {
22+ const refs = workerReferences . get ( event . data . error . id )
23+ if ( refs ) {
24+ const error = new Error ( event . data . error . message )
25+
26+ for ( const ref of refs ) {
27+ const worker = ref . deref ( )
28+ if ( ! worker ) {
29+ refs . delete ( ref )
30+ } else {
31+ worker . dispatchEvent (
32+ new ErrorEvent ( 'error' , {
33+ error,
34+ message : error . message
35+ } )
36+ )
37+ }
38+ }
39+
40+ if ( refs . size === 0 ) {
41+ workerReferences . delete ( event . data . error . id )
2542 workers . delete ( event . data . error . id )
2643 } else {
27- worker . dispatchEvent (
28- new ErrorEvent ( 'error' , {
29- error : new Error ( event . data . error . message )
30- } )
31- )
44+ updateWorkerReference ( event . data . error . id , refs )
3245 }
3346 }
3447 }
3548} )
3649
37- export async function init ( sharedWorker , options ) {
38- const currentWindow = await application . getCurrentWindow ( )
39- const window = await getContextWindow ( )
40- await currentWindow . send ( {
41- event : 'connect' ,
42- window : window . index ,
43- value : {
44- connect : {
45- scriptURL : options . scriptURL ,
46- client : client . toJSON ( ) ,
47- name : options . name ,
48- port : serialize (
49- ipc . findIPCMessageTransfers ( new Set ( ) , sharedWorker . channel . port2 )
50- ) ,
51- id : sharedWorker . id
52- }
50+ // Register before the first await so early module failures reach every
51+ // SharedWorker object using the same URL and name.
52+ function registerWorker ( sharedWorker ) {
53+ let refs = workerReferences . get ( sharedWorker . id )
54+ if ( ! refs ) {
55+ refs = new Set ( )
56+ workerReferences . set ( sharedWorker . id , refs )
57+ }
58+
59+ const ref = new WeakRef ( sharedWorker )
60+ refs . add ( ref )
61+ workers . set ( sharedWorker . id , ref )
62+ return { ref, refs }
63+ }
64+
65+ function unregisterWorker ( sharedWorker , registration ) {
66+ const refs = workerReferences . get ( sharedWorker . id )
67+ if ( refs === registration . refs ) {
68+ refs . delete ( registration . ref )
69+
70+ if ( refs . size === 0 ) {
71+ workerReferences . delete ( sharedWorker . id )
72+ workers . delete ( sharedWorker . id )
73+ } else {
74+ updateWorkerReference ( sharedWorker . id , refs )
75+ }
76+ }
77+ }
78+
79+ function updateWorkerReference ( id , refs ) {
80+ const current = workers . get ( id )
81+ if ( current && refs . has ( current ) && current . deref ( ) ) {
82+ return
83+ }
84+
85+ for ( const ref of refs ) {
86+ if ( ref . deref ( ) ) {
87+ workers . set ( id , ref )
88+ return
5389 }
54- } )
90+ }
5591
56- ipc . IPCMessagePort . transfer ( sharedWorker . channel . port2 )
57- workers . set ( sharedWorker . id , new WeakRef ( sharedWorker ) )
92+ workerReferences . delete ( id )
93+ workers . delete ( id )
94+ }
95+
96+ export async function init ( sharedWorker , options ) {
97+ const registration = registerWorker ( sharedWorker )
98+
99+ try {
100+ const currentWindow = await application . getCurrentWindow ( )
101+ const window = await getContextWindow ( )
102+ const port = serialize (
103+ ipc . findIPCMessageTransfers ( new Set ( ) , sharedWorker . channel . port2 )
104+ )
105+
106+ // Serialize before marking the sending endpoint as transferred, then
107+ // activate the local relay before the receiving worker can reply.
108+ ipc . IPCMessagePort . transfer ( sharedWorker . channel . port2 )
109+
110+ await currentWindow . send ( {
111+ event : 'connect' ,
112+ window : window . index ,
113+ value : {
114+ connect : {
115+ scriptURL : options . scriptURL ,
116+ client : client . toJSON ( ) ,
117+ name : options . name ,
118+ port,
119+ id : sharedWorker . id
120+ }
121+ }
122+ } )
123+ } catch ( err ) {
124+ unregisterWorker ( sharedWorker , registration )
125+ try {
126+ sharedWorker . channel . port1 . close ( )
127+ sharedWorker . channel . port2 . close ( )
128+ } catch { }
129+ throw err
130+ }
58131}
59132
60133export class SharedWorkerMessagePort extends ipc . IPCMessagePort {
@@ -94,20 +167,21 @@ export class SharedWorker extends EventTarget {
94167 }
95168
96169 const url = new URL ( aURL , location . origin )
97- const id = crypto . murmur3 ( url . origin + url . pathname )
170+ const name =
171+ typeof nameOrOptions === 'string'
172+ ? nameOrOptions
173+ : typeof nameOrOptions ?. name === 'string'
174+ ? nameOrOptions . name
175+ : null
176+ const id = crypto . murmur3 ( `${ url . toString ( ) } \0${ name ?? '' } ` )
98177
99178 // @ts -ignore
100179 super ( url . toString ( ) , nameOrOptions )
101180
102181 this . #id = id
103182 this . #ready = init ( this , {
104183 scriptURL : url . toString ( ) ,
105- name :
106- typeof nameOrOptions === 'string'
107- ? nameOrOptions
108- : typeof nameOrOptions ?. name === 'string'
109- ? nameOrOptions . name
110- : null
184+ name
111185 } )
112186 }
113187
0 commit comments